#ifndef __POLL_SVR_H__
#define __POLL_SVR_H__

#include <iostream>
#include <sys/select.h>
#include <poll.h>
#include "Log.hpp"
#include "Sock.hpp"

#define FD_NONE -1

using namespace std;
// select 我们只完成读取，写入和异常不做处理 -- epoll(写完整)
class PollServer
{
public:
    static const int nfds = 100;

public:
    PollServer(const uint16_t &port = 8080)
        : _port(port), _nfds(nfds)
    {
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);
        logMessage(DEBUG, "%s", "create base socket success");

        _fds = new struct pollfd[_nfds];
        for (int i = 0; i < _nfds; i++)
        {
            _fds[i].fd = FD_NONE;
            _fds[i].events = _fds[i].revents = 0;
        }
        _fds[0].fd = _listensock;
        _fds[0].events = POLLIN;
        _timeout = 1000;
    }

    void Start()
    {
        while (true)
        {
            int n = poll(_fds, _nfds, _timeout);
            switch (n)
            {
            case 0:
                logMessage(DEBUG, "%s", "time out...");
                break;
            case -1:
                logMessage(WARNING, "select error: %d : %s", errno, strerror(errno));
                break;
            default:
                // 成功的
                HandlerEvent();
                break;
            }
        }
    }

    ~PollServer()
    {
        if (_listensock >= 0)
            close(_listensock);
        if (_fds)
            delete[] _fds;
    }
private:
    void HandlerEvent() // fd_set 是一个集合，里面可能会存在多个sock
    {
        for (int i = 0; i < _nfds; i++)
        {
            if (_fds[i].fd == FD_NONE) // 1. 去掉不合法的fd
                continue;
            // 2. 合法的fd不一定就绪了
            if (_fds[i].revents & POLLIN) // 如果fd读事件就绪
            {
                if(_fds[i].fd == _listensock) // 读事件就绪：连接事件到来，accept
                {
                    Accepter();
                }
                else // 读事件就绪：INPUT事件到来，read / recv
                {
                    Recver(i);
                }
            }
        }
    }
    void Accepter()
    {
        string clientip;
        uint16_t clientport = 0;
        // listensock上面的读事件就绪了，表示可以读取了，获取新连接了
        int sock = Sock::Accept(_listensock, &clientip, &clientport); // 这里进行accept不会阻塞
        if (sock < 0)
        {
            logMessage(WARNING, "accept error");
            return;
        }
        logMessage(DEBUG, "get a new line success : [%s:%d] : %d", clientip.c_str(), clientport, sock);
        int pos = 1;               // 规定了 _fd_array[0] = _listensock; 不用管
        for (; pos < _nfds; pos++) // 将fd放入到数组中 -> 找一个合法的位置
        {
            if (_fds[pos].fd == FD_NONE)
                break;
        }
        if (pos == _nfds) // 可以对struct pollfd进行自动扩容,或者直接改数组大小,这里不处理
        {
            logMessage(WARNING, "%s:%d", "poll server already full, close: %d", sock);
            close(sock);
        }
        else // 找到了合法的位置
        {
            _fds[pos].fd = sock;
            _fds[pos].events = POLLIN;
        }
    }
    void Recver(int pos)
    {
        // 读事件就绪：INPUT事件到来、recv，read
        logMessage(DEBUG, "message in, get IO event: %d", _fds[pos]);
        // 此时poll已经帮我们进行了事件检测，fd上的数据一定是就绪的，即 本次 不会被阻塞
        // 怎么保证以读到了一个完整的报文呢？ -> 模拟实现epoll的时候再考虑
        char buffer[1024];
        int n = recv(_fds[pos].fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0) // 正常读取
        {
            buffer[n] = 0;
            logMessage(DEBUG, "client[%d]# %s", _fds[pos].fd, buffer);
        }
        else if (n == 0) // 对端把链接关了
        {
            logMessage(DEBUG, "client[%d] quit, me too...", _fds[pos]);
            close(_fds[pos].fd);    // 关闭不需要的fd
            _fds[pos].fd = FD_NONE; // 不要让poll帮我关心当前的fd了
            _fds[pos].events = 0;
        }
        else // 读取错误
        {
            logMessage(WARNING, "%d sock recv error, %d : %s", _fds[pos].fd, errno, strerror(errno));
            close(_fds[pos].fd);    // 关闭不需要的fd
            _fds[pos].fd = FD_NONE; // 不要让poll帮我关心当前的fd了
            _fds[pos].events = 0;
        }
    }
    void DebugPrint() // 打印一下数组里合法的fd
    {
        cout << "_fd_array[]: ";
        for (int i = 0; i < _nfds; i++)
        {
            if (_fds[i].fd == FD_NONE)
                continue;
            cout << _fds[i].fd << " ";
        }
        cout << endl;
    }
private:
    uint16_t _port;
    int _listensock;
    struct pollfd *_fds;
    int _nfds;
    int _timeout;
};

#endif